﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// 
    /// </summary>
    public enum COLORDISTANCETRANSFORM_MODE
    {
        /// <summary>
        /// 
        /// </summary>
        DEFAULT,
        /// <summary>
        /// 
        /// </summary>
        DISTANCE
    }

    /// <summary>
    /// This is a filter created on my own.
    /// It will measure the distance in colors of the window until a 
    /// pixel with a similar color appears
    /// </summary>
    public class ColorDistanceTransform : BaseImageFilter
    {
        protected int m_nColorDistanceThreshold = 7;
        protected int m_nWindowSize = 13;
        protected COLORDISTANCETRANSFORM_MODE M_MODE = COLORDISTANCETRANSFORM_MODE.DISTANCE;

        /// <summary>
        /// Initializes a new instance of the <see cref="ColorDistanceTransform"/> class.
        /// </summary>
        public ColorDistanceTransform()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ColorDistanceTransform"/> class.
        /// </summary>
        /// <param name="_nColorDistanceThreshold">The _n color distance threshold.</param>
        public ColorDistanceTransform(int _nColorDistanceThreshold)
        {
            m_nColorDistanceThreshold = _nColorDistanceThreshold;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ColorDistanceTransform"/> class.
        /// </summary>
        /// <param name="_colorDistanceTransform">The _color distance transform.</param>
        internal ColorDistanceTransform(ColorDistanceTransform _colorDistanceTransform)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        public COLORDISTANCETRANSFORM_MODE Mode
        {
            get { return M_MODE; }
            set { M_MODE = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        public int Threshold
        {
            get { return m_nColorDistanceThreshold; }
            set { m_nColorDistanceThreshold = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {

            UnsafeBitmap bitmapCloned = (UnsafeBitmap)_bitmap.Clone();

            switch (M_MODE)
            {
                case COLORDISTANCETRANSFORM_MODE.DEFAULT:
                    DefaultMode(_bitmap);
                    break;

                case COLORDISTANCETRANSFORM_MODE.DISTANCE:
                    Dictionary<Color, Point> aColorPoint = new Dictionary<Color, Point>();
                    Color color = Color.Transparent;
                    int nHeight = _bitmap.Height;
                    int nWidth = _bitmap.Width;

                    int y=0;
                    int x=0;
                    //Cache values here...
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            color = _bitmap.GetPixel(x, y);
                            if (!aColorPoint.ContainsKey(color))
                            {
                                aColorPoint.Add(color, new Point(x,y));
                            }
                            else
                            {
                                Point pt = aColorPoint[color];
                                pt.X = (int)((pt.X + x)/2.0f);
                                pt.Y = (int)((pt.Y + y)/2.0f);
                                aColorPoint[color] = pt;
                            }
                        }
                    }

                    double dMaxDistance = ImageMath.GetDistance(new PointF(0,0), new PointF(nWidth,nHeight));
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            color = _bitmap.GetPixel(x, y);
                            double dDistance = ImageMath.GetDistance(aColorPoint[color], new PointF(x, y));
                            int nDistance = (int)(dDistance / dMaxDistance * 255);
                            int nR = (int)(nDistance / dMaxDistance * color.R);
                            int nG = (int)(nDistance / dMaxDistance * color.G);
                            int nB = (int)(nDistance / dMaxDistance * color.B);
                            if (nR > 255) nR = 255;
                            if (nG > 255) nG = 255;
                            if (nB > 255) nB = 255;
                            _bitmap.SetPixel(x, y, Color.FromArgb(nR, nG, nB));
                        }
                    }
                    break;
            }
            return _bitmap;
        }

        /// <summary>
        /// Defaults the mode.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        protected void DefaultMode(UnsafeBitmap _bitmap)
        {
            int nXTemp = 0;
            int nYTemp = 0;

            int y = 0;
            int x = 0;
            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;
            int kx = 0;
            int ky = 0;
            for (y = m_nWindowSize; y < nHeight - m_nWindowSize; y++)
            {
                for (x = m_nWindowSize; x < nWidth - m_nWindowSize; x++)
                {
                    int nColor = _bitmap.GetPixelDirectInt(x, y);

                    int nCount = 0;
                    for (ky = 0; ky < m_nWindowSize; ky++)
                    {
                        for (kx = 0; kx < m_nWindowSize; kx++)
                        {
                            nXTemp = x + kx;
                            nYTemp = y + kx;

                            int nColorDistanceKernel = _bitmap.GetPixelDirectInt(nXTemp, nYTemp);
                            int nTempColorDistance = nColor * nColor - nColorDistanceKernel * nColorDistanceKernel;
                            if (nTempColorDistance < 0) nTempColorDistance *= -1;
                            if (nColorDistanceKernel <= m_nColorDistanceThreshold)
                            {
                                nCount++;
                            }
                        }
                    }

                    int nNormalize = -1;
                    if (nCount > 1)
                    {
                        nNormalize = nCount / m_nWindowSize * m_nWindowSize * 255;
                    }
                    _bitmap.SetGrayPixel(x, y, nNormalize);
                }
            }
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ColorDistanceTransform(this);
        }
    }
}
